﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace RayTracing
{    
    public partial class Main : Form
    {
        // Define Infinity
        public static float infinity = 999999999999999999;
        //Anti Aliasing Passes
        public static int passes = 1;
        public static int displayPasses = 0;
        public static bool previewOn = true;
        public int displayCount = 0;
        // Flags
        public static bool render = false;
        public static bool sceneSelected = false;
        public static bool cameraMoved = false;
        public Random randoms = new Random();

        Scene sceneImage;

        Scene[] allScenes = { new BasicScene(),new ParaTestScene(), new CylinderScene(), new TriangleScene(), new BasicAnimation() };


        // Camera variables
        public static Camera camera = new Camera();

        // Image variables
        public static int SIZE = 500;
        public Bitmap bitmap;
        public static Bitmap previewBitmap;
        public Color[,] pixelGrid, normalizePixGrid;

        //slider variables;
        String[] sliderLabels = new String[5];
        float slider1Value;
        float slider2Value;
        float slider3Value;
        float slider4Value;
        float slider5Value;

        // theScene statics
        public static List<SceneObject> theScene;
        public static List<Light> lightSources;


        public Main()
        {
            InitializeComponent();
            comboBoxInitialization();
        }

        public void comboBoxInitialization()
        {
            foreach (Scene s in allScenes)
            {
                comboBoxScene.Items.Add(s.sceneName());
            }
        }

        public void renderScene()
        {
            Init();
            Run();
        }

        public void Init()
        {
            // Camera variables
            //distance = -5;
            //cameraPosition = new P3(0, distance, 0);

            // Image variables
            bitmap = new Bitmap(SIZE, SIZE);
            previewBitmap = new Bitmap(SIZE, SIZE);
            pixelGrid = new Color[SIZE, SIZE];
            normalizePixGrid = new Color[SIZE, SIZE];

        }

        public void Run()
        {   
            shootFrame(1);
            gridNormalize();
        }

        public void shootFrame(int c) //Anti Aliasing Method       
        {
            float dx = (float)randoms.NextDouble(), dz = (float)randoms.NextDouble();
            P3 gridPoint;
            Ray ray;
            for (int i = 0; i < pixelGrid.GetLength(0); i++)
            {
                for (int j = 0; j < pixelGrid.GetLength(1); j++)
                {
                   
                    gridPoint = new P3(((i + dx) / (SIZE * 1.0f) * 2 - 1), 0, ((j + dz) / (SIZE * 1.0f) * 2 - 1)); //added randoms dx and dz
                    ray = new Ray(camera.pos, P3.sub(gridPoint, camera.pos));
                    Color color = shootRay(ray);
                    pixelGrid[i, j] = Color.average(color, pixelGrid[i, j], 1.0f / c);  //created average method in color for this
                }
            }
        }

        public void gridNormalize()
        {
            float brightest = 0.00000000000000000001f;
            for (int i = 0; i < SIZE; i++)
            {
                for (int j = 0; j < SIZE; j++)
                {
                    brightest = Math.Max(brightest, Color.intensity(pixelGrid[i, j]));
                }
            }

            for (int i = 0; i < SIZE; i++)
            {
                for (int j = 0; j < SIZE; j++)
                {
                    normalizePixGrid[i, j] = Color.scale(pixelGrid[i, j], 1 / brightest);
                }
            }

        }

        public static Color shootRay(Ray ray)
        {
            float dist;
            SceneObject obj = closest(ray);

            if (obj == null) return Color.BLACK;
            dist = obj.distance(ray);
            if (dist == infinity)
            {
                return Color.BLACK;
            }
            else
            {
                return obj.colorAt(ray.travel(dist), ray);
            }
        }

        public static SceneObject closest(Ray ray)
        {
            SceneObject sceneObj = null;
            float dist, smallestDist = infinity;

            foreach (SceneObject obj in theScene)
            {
                if ((dist = obj.distance(ray)) < smallestDist)
                {
                    smallestDist = dist;
                    sceneObj = obj;
                }
            }
            return sceneObj;
        }

        public static bool occluded(Ray r, SceneObject standingOn, float distToLight)
        {
            
            foreach (SceneObject obj in theScene)
            {
                if (obj != standingOn && obj.distance(r) < distToLight)
                    return true;
            }
            return false;
        }

        private void tickBar1_ValueChanged(object sender, EventArgs e)
        {
            slider1Value = tickBar1.Value;
            preview();
        }

        private void tickBar2_ValueChanged(object sender, EventArgs e)
        {
            slider2Value = tickBar2.Value;
            preview();
        }

        private void tickBar3_ValueChanged(object sender, EventArgs e)
        {
            slider3Value = tickBar3.Value;
            preview();
        }

        private void tickBar4_ValueChanged(object sender, EventArgs e)
        {
            slider4Value = tickBar4.Value;
            preview();
        }

        private void tickBar5_ValueChanged(object sender, EventArgs e)
        {
            slider5Value = tickBar5.Value;
            preview();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            if (sceneSelected)
            {
                for (int i = 0; i < SIZE; i++)
                {
                    for (int j = 0; j < SIZE; j++)
                    {
                        bitmap.SetPixel(i, j, Color.toRGB(normalizePixGrid[i, j]));
                    }
                }
                sceneBox.BackgroundImage = bitmap;
                previewOn = true;
            }
        }

        private void pictureBoxPreview_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            if (sceneSelected)
            {
                for (int i = 0; i < SIZE; i++)
                {
                    for (int j = 0; j < SIZE; j++)
                    {
                        previewBitmap.SetPixel(i, j, Color.toRGB(pixelGrid[i, j]));
                    }
                }
                pictureBoxPreview.BackgroundImage = previewBitmap;
            }
        }
//animation-------------------------------------------------------------------
        
        public static AnimationParamaters animationParamaters = new AnimationParamaters();
        public static int currentFrame;
        float currentTime;
        public static bool animate = false;
        bool previewFlag = false;
        public static List<Bitmap> listOfScenes = new List<Bitmap>();
        public static int movieFrame = 0;
        public static bool moviePlay = false;
        public static bool movieTrackBarChanged = false;

        private void buttonRender_Click(object sender, EventArgs e)
        {
            currentFrame = 0;
            currentTime = 0;
            previewFlag = false;
            renderThis();
        }
        
        private void comboBoxScene_SelectedIndexChanged(object sender, EventArgs e)
        {
            sceneImage = allScenes[comboBoxScene.SelectedIndex];
            sliderLabels = sceneImage.SliderLabels();
            labelTickBar1.Text = sliderLabels[0];
            labelTickBar2.Text = sliderLabels[1];
            labelTickBar3.Text = sliderLabels[2];
            labelTickBar4.Text = sliderLabels[3];
            labelTickBar5.Text = sliderLabels[4];
            animationParamaters = sceneImage.getAnimationParamaters();
            currentFrame = 0;
            currentTime = 0;
            preview();
        }
        int n = 0;
        public void nextFrame()
        {
            if (animate)
            {
                currentFrame++;
                currentTime += animationParamaters.timeBetweenFrames;
                if (previewFlag)
                {
                    preview();
                    if (currentFrame >= animationParamaters.totalFrames)
                    {
                        currentFrame = 0;
                        currentTime = 0;
                        preview();
                    }
                }
            else
            {
                if (currentFrame <= animationParamaters.totalFrames)
                {
                    renderThis();
                    Console.Out.WriteLine("Buffering Video");
                    listOfScenes.Add(bitmap);
                }
                else
                {
                    Console.Out.WriteLine("Done Buffering");
                    animate = false;
                    Form AnimationPropertiesWindow = new AnimationPropertiesForm();
                    AnimationPropertiesWindow.Text = "Animation Properties";
                    AnimationPropertiesWindow.Show();
                }
             }
          }
        }
        public void savePictures()
        {
            foreach (Bitmap bit in listOfScenes)
            {
                bit.Save(string.Format("frame {0:D5}.jpg", n));
                n++;
            }
        }
        private void checkBoxAnimate_CheckedChanged(object sender, EventArgs e)
        {
            if (!animate)
            {
                animate = true;
                Console.WriteLine("Animate on.");
                buttonAnimatePreview.Visible = true;
            }
            else
            {
                animate = false;
                Console.WriteLine("Animate off.");
                buttonAnimatePreview.Visible = false;
            }
        }
        private void timer_Tick(object sender, EventArgs e)
        {
            if (animate)
            {
                nextFrame();
            }

            if (moviePlay)
            {
                sceneBox.Image = listOfScenes.ElementAt<Bitmap>(movieFrame%animationParamaters.totalFrames);
                sceneBox.Invalidate();
                movieFrame++;

            }

            if (movieTrackBarChanged)
            {
                sceneBox.Image = listOfScenes.ElementAt<Bitmap>(movieFrame%animationParamaters.totalFrames);
                sceneBox.Invalidate();
            }

        }
        private void buttonAnimatePreview_Click(object sender, EventArgs e)
        {
            timer.Enabled = true;
            previewFlag = true;
        }

        public void renderThis()
        {
            if (sceneSelected)
            {
                if (animate)
                {
                    timer.Enabled = true;
                }
                if (sceneSelected)
                {
                    SIZE = 500;
                    sceneImage.createScene(out theScene, out lightSources,out camera, currentTime, slider1Value, slider2Value, slider3Value, slider4Value, slider5Value);
                    renderScene();
                    sceneBox.Refresh();
                }
            }
        }
        

                //Method to update pictureBoxPreview as camera values are changed
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (cameraMoved)
            {
                preview();
                cameraMoved = false;
            }
        }

        public void preview()
        {
                SIZE = 75;
                sceneImage.createScene(out theScene, out lightSources, out camera, currentTime, slider1Value, slider2Value, slider3Value, slider4Value, slider5Value);
                renderScene();
                sceneSelected = true;
                pictureBoxPreview.Refresh();
        }

        private void antiAliasingOptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form antiAliasing = new AntiAliasingForm();
            antiAliasing.Text = "Anti-Aliasing options";
            antiAliasing.Show();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit(); 
        }

        private void prefToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form AnimationPropertiesWindow = new AnimationPropertiesForm();
            AnimationPropertiesWindow.Text = "Animation Properties";
            AnimationPropertiesWindow.Show();
        }

        private void saveAnimationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            
            saveFileDialog1.ShowDialog();
        }

        private void loadAnimationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();
        }

        private void cameraControlsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form cameraControls = new CameraControls();
            cameraControls.Text = "Camera Controls";
            cameraControls.Show();
        }
    }
}
